package com.steven.logserver.scheduler;

import com.steven.logserver.config.ZookeeperConfig;
import org.apache.curator.framework.CuratorFramework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 定时任务
 *
 * @author qinxh
 * @date 2020/08/31
 */
@Component
public class SchedulerTask {

    private static final Logger logger = LoggerFactory.getLogger(SchedulerTask.class);

    @Value("${log.netty.server.file-folder}")
    private String fileFolder;

    @Value("${log.netty.server.node.Path}")
    private String nodePath;

    @Value("${log.netty.bind.port}")
    private Integer bindPort;

    @Value("${log.netty.server.file-duration}")
    private Integer fileDuration;

    private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy_MM_dd");

    private CuratorFramework client = ZookeeperConfig.getClient();

    /**
     * 同步一下当前日志服务的日志文件大小。首次运行延迟zk会话超时的时间
     */
    @Scheduled(initialDelayString = "${log.netty.server.sync-file-size-init}",
               fixedRateString = "${log.netty.server.sync-file-size-interval}")
    public void synchronousFileSize() throws Exception {

        // 日志文件当前大小（byte） 作为为节点数据。如果日志文件不存在，则设置为0
        byte[] data = "0".getBytes();
        String filePath = fileFolder + "/log_" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy_MM_dd")) + ".file";
        File file = new File(filePath);
        if(file.exists()){
            RandomAccessFile reader = new RandomAccessFile(filePath, "r");
            data = String.valueOf(reader.getChannel().size()).getBytes();
            reader.getChannel().close();
            reader.close();
        }
        // 设定 ip + netty 服务绑定端口作为服务节点名
        String path = nodePath + InetAddress.getLocalHost().getHostAddress() + ":" + bindPort;
        client.setData().forPath(path, data);
        logger.info("成功更新日志文件大小：{}到对应zk节点：{}", new String(data), path);
    }

    /**
     * 每天零点生成当天的日志文件，并删除 除 fileDuration 值天数之内的文件
     */
    @Scheduled(cron="0 10 0 * * ? ")
    public void process() {

        File baseFile = new File(fileFolder);
        if (!baseFile.exists() && !baseFile.isDirectory()){
            logger.info("netty日志收集服务端日志文件存放位置 {} 不存在", fileFolder);
            if(baseFile.mkdir()) {
                logger.info("创建文件夹成功!");
            }else{
                logger.error("创建文件夹失败!");
            }
        }
        // 创建当天的日志文件
        LocalDate now = LocalDate.now();
        String s = fileFolder + "/log_" + now.format(formatter) + ".file";
        File nowFile = new File(s);
        if(!nowFile.exists()){
            try {
                if(nowFile.createNewFile()){
                    logger.info("{}：创建文件成功!", s);
                } else {
                    logger.error("{}：创建文件失败!", s);
                }
            } catch (IOException e) {
                logger.error("{}：创建文件失败!", e.getMessage());
            }
        }
        // delete 除去 fileDuration 值天数之内的文件
        List<String> collect = IntStream.range(0, fileDuration).mapToObj(m -> {
            LocalDate localDate = now.plusDays(- m);
            return fileFolder + "/log_" + localDate.format(formatter) + ".file";
        }).collect(Collectors.toList());
        File[] files = baseFile.listFiles();
        if(files != null && files.length > 0) {
            List<File> list = Arrays.stream(files).filter(f -> !collect.contains(
                    f.getAbsolutePath().replaceAll("\\\\", "/"))).collect(Collectors.toList());
            //TIPS: delete：一旦调用,程序立即执行删除操作；
            //      deleteOnExit：被调用时,相当于对deleteOnExit做了一个生声明, 不会马上执行删除操作, 而是程序运行结束,
            //      JVM终止时才真正调用删除操作。即该方法是将删除的命令缓存了一下，到服务停止的时候再进行操作！
            list.forEach(File::delete);
            logger.info("成功删除日志文件：{}个", list.size());
        }

    }
}
